package com.ycy.skip;

import com.ycy.BaseNoListenerTest;
import com.ycy.BaseTest;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.engine.*;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.event.impl.FlowableTaskEventBuilder;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.flowable.task.service.impl.persistence.entity.TaskEntityManager;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 流程跳转 到指定 userTask
 */
public class SkipDemo extends BaseNoListenerTest {

  static Logger logger = LoggerFactory.getLogger(SkipDemo.class);

  @Test
  public void creat() {
    creatProcDef();
//    String procInst = createProcInst();
//    querTask(procInst);
//    passB(procInst);
  }

  @Test
  public void tryskip() {
    skipCtoB("4", "C", "B");
  }

  @Test
  public void pass() {
    //org.flowable.common.engine.api.FlowableException: Unknown property used in expression: ${nrOfCompletedInstances > 0}
    passB("15016");
  }

  @Test
  public void passC() {
    //org.flowable.common.engine.api.FlowableException: Unknown property used in expression: ${nrOfCompletedInstances > 0}
    passC("15016");
  }

  @Test
  public void query() {
    querTask("4");
  }

  public void creatProcDef() {
    // 部署流程
    RepositoryService repositoryService = processEngine.getRepositoryService();
    Deployment deployment = repositoryService.createDeployment()
            .addClasspathResource("skip/skip.bpmn20.xml")
            .deploy();
    System.out.println(deployment);
  }

  public String createProcInst() {
    RuntimeService runtimeService = processEngine.getRuntimeService();
    HashMap<String, Object> variables = new HashMap<>();
    List<Long> zhixingrens = new ArrayList<>();
    zhixingrens.add(1L);
    zhixingrens.add(2L);
    variables.put("ASSIGNEE_zhixingren1_LIST", zhixingrens);

    ProcessInstance processInstance =
            runtimeService.startProcessInstanceByKey("skiping", variables);
    return processInstance.getProcessInstanceId();
  }

  public void querTask(String processInst) {
    TaskService taskService = processEngine.getTaskService();
    List<Task> list = taskService.createTaskQuery().processInstanceId(processInst).list();
    for (Task task : list) {
      System.out.println(task);
      System.out.println("getExecutionId " + task.getExecutionId());
      System.out.println("variable " + taskService.getVariables(task.getId()));
    }
  }

  public void passB(String processInst) {
    TaskService taskService = processEngine.getTaskService();
    Task b = taskService.createTaskQuery().processInstanceId(processInst).taskDefinitionKey("B").singleResult();
    HashMap<String, Object> variables = new HashMap<>();
    List<Long> zhixingren2s = new ArrayList<>();
    zhixingren2s.add(2L);
    variables.put("ASSIGNEE_zhixingren2_LIST", zhixingren2s);
    taskService.complete(b.getId(), variables);
  }

  public void passC(String processInst) {
    TaskService taskService = processEngine.getTaskService();
    Task b = taskService.createTaskQuery().processInstanceId(processInst).taskDefinitionKey("C").singleResult();
    HashMap<String, Object> variables = new HashMap<>();
    taskService.complete(b.getId(), variables);
  }

//  public void pass(String processInst) {
//    TaskService taskService = processEngine.getTaskService();
//    Task b = taskService.createTaskQuery().processInstanceId(processInst).taskDefinitionKey("C").singleResult();
//    HashMap<String, Object> variables = new HashMap<>();
//    List<Long> zhixingren2s = new ArrayList<>();
//    taskService.complete(b.getId(), variables);
//  }


  public static String OPERATE_TYPE_SUPER_OP = "superOp";

  public void skipCtoB(String procInstId, String taskKey, String target) {

    Task taskEntity =
            processEngine.getTaskService().createTaskQuery().processInstanceId(procInstId).taskDefinitionKey(taskKey)
                    .singleResult();

    Map<String, Object> finalParams = new HashMap<>();

    ManagementService managementService = processEngine.getManagementService();

    managementService.executeCommand((Command<Void>) commandContext -> {

      TaskEntityManager taskEntityManager = org.flowable.task.service.impl.util.CommandContextUtil.getTaskEntityManager();

//      TaskEntity taskEntity = taskEntityManager.findById(bpTask.getTaskId());
//      String procInstId = taskEntity.getProcessInstanceId();
//
//      if (taskEntity == null) {
//        logger.warn("superOp failed. Task instance is not found (taskInstId={})", bpTask.getTaskId());
//        throw new RuntimeException("superOp failed. Task instance is not found (taskInstId=" + bpTask.getTaskId() + ")");
//      }

      List<TaskEntity> tasks = taskEntityManager.findTasksByProcessInstanceId(taskEntity.getProcessInstanceId());

      //给每一个需要删除的task添加传递给listener的参数
      tasks.forEach(e -> e.setVariables(finalParams));

      ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
      ExecutionEntity ee = executionEntityManager.findById(taskEntity.getExecutionId());

      //查询并验证目标任务是否合法
      Process process = ProcessDefinitionUtil.getProcess(ee.getProcessDefinitionId());
      FlowElement targetFlowElement = process.getFlowElement(target, true);
      if (targetFlowElement == null) {
        logger.warn("superOp failed. Target task is not found (target={}, procInstId={})", target, procInstId);
        throw new RuntimeException("superOp failed. Target task is not found (target=" + target + ", procInstId=" + procInstId + ")");
      }

      //删除所有相关任务
      logger.info("start to remove all related tasks (procInstId={}, task size={})", procInstId, tasks.size());
      tasks.forEach(task -> {
//        task.setVariable(RdfaBpCustomVarContants.TASK_AUDIT_REASON, comment);
//        task.setVariable(RdfaBpCustomVarContants.TASK_OPERATE_TYPE, finalOperateType);
//        task.setVariable(RdfaBpCustomVarContants.TASK_OPERATE_EXECUTOR, operator.getUserId());
        CommandContextUtil.getHistoryManager().recordTaskEnd(task, ee, OPERATE_TYPE_SUPER_OP);
        CommandContextUtil.getIdentityLinkService().deleteIdentityLinksByTaskId(task.getId());
        CommandContextUtil.getTaskService().deleteTask(task, true);
        ExecutionEntity innerExec = executionEntityManager.findById(task.getExecutionId());
        CommandContextUtil.getExecutionEntityManager().deleteExecutionAndRelatedData(innerExec, OPERATE_TYPE_SUPER_OP);
      });
      logger.info("end to remove all related tasks (procInstId={}, task size={})", procInstId, tasks.size());

      //查询并删除相关Execution
      List<ExecutionEntity> execs = executionEntityManager.findChildExecutionsByProcessInstanceId(procInstId);
      logger.info("start to remove all child executions (procInstId={}, Execution size={})", procInstId, execs.size());
      execs.forEach(e -> CommandContextUtil.getExecutionEntityManager().deleteExecutionAndRelatedData(e, OPERATE_TYPE_SUPER_OP));
      logger.info("end to remove all child executions (procInstId={}, Execution size={})", procInstId, execs.size());

      ProcessInstance processInstance = processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceId(
              procInstId).singleResult();

      //创建新的Execution
      ExecutionEntity root = executionEntityManager.findByRootProcessInstanceId(procInstId);
      ExecutionEntity exe = createExecution(targetFlowElement, root);
      exe.setCurrentFlowElement(targetFlowElement);
      exe.setVariables(finalParams);

      FlowableEngineAgenda agenda = CommandContextUtil.getAgenda();
      agenda.planContinueProcessInCompensation(ee);

      //创建新的目标任务
      TaskEntityImpl newTask = new TaskEntityImpl();
      newTask.setExecutionId(exe.getId());
      newTask.setProcessInstanceId(procInstId);
      newTask.setAssigneeValue(processInstance.getStartUserId());
      newTask.setTaskDefinitionKey(target);
      newTask.setName(targetFlowElement.getName());
      newTask.setTaskDefinitionId(targetFlowElement.getId());
      newTask.setDescription(targetFlowElement.getDocumentation());
      newTask.setCreateTime(new Date());
      newTask.setProcessDefinitionId(processInstance.getProcessDefinitionId());

      if (targetFlowElement instanceof Activity && ((Activity) targetFlowElement).hasMultiInstanceLoopCharacteristics()) {
        newTask.setCountEnabled(true);
        newTask.setVariableCount(2);
        newTask.setSubTaskCount(1);
      }
      CommandContextUtil.getTaskService().insertTask(newTask, true);
      CommandContextUtil.getHistoryManager().recordTaskCreated(newTask, exe);
      CommandContextUtil.getEventDispatcher().dispatchEvent(
              FlowableTaskEventBuilder.createEntityEvent(FlowableEngineEventType.TASK_CREATED, newTask));

//      FlowableEvent event = new BpSuperOpEvent(procInstId, operator.getUserId(), finalOperateType, comment, finalParams);
//      CommandContextUtil.getEventDispatcher().dispatchEvent(event);
//      logger.info("sent BpSuperOpEvent event (procInstId={},userId={},operateType={},comment={},finalParams={})", procInstId, operator.getUserId(), finalOperateType, comment, finalParams);

      return null;
    });
  }


  private ExecutionEntity createExecution(FlowElement targetFlowElement, ExecutionEntity root) {
    logger.info("start createExecution (targetFlowElement={})", targetFlowElement.getName());
    Stack<FlowElementsContainer> stack = new Stack<>();
    FlowElementsContainer container = targetFlowElement.getParentContainer();
    while (true) {
      if (container instanceof Process) {
        logger.info("createExecution reach process (process container={}, name={})", ((Process) container).getId(), ((Process) container).getName());
        break;
      } else if (container instanceof FlowElement) {
        logger.info("createExecution push to stack (container={})", ((FlowElement) container).getId());
        stack.push(container);
        container = ((FlowElement) container).getParentContainer();
      }
    }

    ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
    ExecutionEntity subExe = root;
    while (!stack.empty()) {
      container = stack.pop();
      logger.info("createExecution pop stack (process container={}, name={})", ((FlowElement) container).getId(), ((FlowElement) container).getName());
      if (container instanceof BaseElement) {
        BaseElement base = (BaseElement) container;
        Optional<? extends ExecutionEntity> childExecution = root.getExecutions().stream().filter(e -> e.getId().equals(base.getId())).findFirst();

        if (childExecution.isPresent()) {
          logger.info("createExecution child execution exist (executionId={})", childExecution.get().getId());
          subExe = childExecution.get();
        } else {
          subExe = executionEntityManager.createChildExecution(subExe);
          logger.info("createExecution child execution is not exist, create one (child executionId={})", subExe.getId());
          subExe.setStartActivityId(base.getId());

          //if last parent
          if (stack.empty()) {
            if (targetFlowElement instanceof Activity && ((Activity) targetFlowElement).hasMultiInstanceLoopCharacteristics()) {
              subExe.setMultiInstanceRoot(true);
            }
          }

          executionEntityManager.update(subExe);
        }
      }
    }
    subExe = executionEntityManager.createChildExecution(subExe);
    logger.info("end createExecution (subExe={})", subExe.getId());
    return subExe;
  }


}
